﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Dune2013
{
    public class Actor
    {
        public Behavior currentBehavior,lastBehavior,globalBehavior;
        public Texture2D tex;
        public Vector2 position;
        float rotation;
        public Vector2 velocity;
        public int cansancio;
        public Actor targetAct;
        public float speed = 1;
        public float maxTurnSpeed=1.0f;
        public float rangoVision = 1000.0f, rangoRepulsion = 200.0f;
        public List<Vector2> wayPoints = new List<Vector2>();
        public float scale = 1.0f;
        public float maxForce = 10.0f;
        public float maxSpeed = 5;
        public float type = -1;
        public bool interrupted = false;
        public Actor()
        {
            position = new Vector2(0,0);
            velocity = new Vector2(1,0);
        }
        public virtual void draw(SpriteBatch sb)
        {
            if (tex != null)
                sb.Draw(tex, position,null, Color.White,rotation,new Vector2(tex.Width/2,tex.Height/2),scale,SpriteEffects.None,0);
        }
        public void changeBehavior(Behavior newBehavior)
        {
            if (currentBehavior!=null)
            currentBehavior.exit(this);
            currentBehavior = newBehavior;
            if (newBehavior != null)
            {
                newBehavior.enter(this);
            }
        }
        public void resumeBehavior()
        {
            currentBehavior.exit(this);
            switch (lastBehavior.typeBehavior) { 
                case Behavior.TypeBehavior.IrA:
                    currentBehavior = lastBehavior; 
                    currentBehavior.enter(this);
                    break;
                case Behavior.TypeBehavior.Trabajo:
                    currentBehavior = lastBehavior.getIrABehavior();
                    currentBehavior.enter(this);
                    break;
            }
            interrupted = false; //para que vuelva a hacer el global
        }

        public void interruptBehavior(Behavior newBehavior)
        {
            //currentBehavior.exit(this);
            lastBehavior=currentBehavior;
            interrupted = true; //para que deje de hacer el global
            currentBehavior = newBehavior;
            currentBehavior.enter(this);
        }
        public virtual void update(int dTime)
        {
            
            position += velocity*speed;
            if (globalBehavior!= null && !interrupted)
                globalBehavior.execute(dTime, this);
            if (currentBehavior!=null)
                currentBehavior.execute(dTime,this);
        }

        protected void tryRotation(float targetRot)
        { 
            float rotDiff=targetRot-rotation;
            while (rotDiff>MathHelper.Pi)
                rotDiff-=MathHelper.TwoPi;
            while (rotDiff<-MathHelper.Pi)
                rotDiff+=MathHelper.TwoPi;
            rotDiff=MathHelper.Clamp(rotDiff,-maxTurnSpeed,+maxTurnSpeed);
            rotation+=rotDiff;

            velocity=Vector2.Transform(velocity, Matrix.CreateRotationZ(rotDiff));
        }
        protected void tryOrientation(Vector2 targetForce)
        {
            float rotDiff = (float)Math.Atan2(targetForce.Y,targetForce.X) - rotation;
            while (rotDiff > MathHelper.Pi)
                rotDiff -= MathHelper.TwoPi;
            while (rotDiff < -MathHelper.Pi)
                rotDiff += MathHelper.TwoPi;
            rotDiff = MathHelper.Clamp(rotDiff, -maxTurnSpeed, +maxTurnSpeed);
            rotation += rotDiff;
            speed = targetForce.Length();
            speed = MathHelper.Clamp(speed, 0, maxSpeed);
            velocity = Vector2.Transform(velocity, Matrix.CreateRotationZ(rotDiff));

        }
        
        
        public Vector2 getSize() {
            return new Vector2(tex.Width * scale, tex.Height * scale);
        }


        public virtual Vector2 seek(Vector2 seekPosition)
        {
            Vector2 dist;
            dist = seekPosition - this.position;
            if (dist != Vector2.Zero) dist.Normalize();
            return dist * this.speed;
        }
        
        public virtual Vector2 seek(Actor act)
        {
            //return seek(act.position);
            Vector2 dist;
            dist = act.position - this.position;
            if (dist!=Vector2.Zero) dist.Normalize();
            return dist * this.speed;
        }

        public virtual Vector2 flee(Actor act)
        {
            Vector2 dist;
            dist = this.position - act.position;
            if (dist != Vector2.Zero) dist.Normalize();
            return dist * this.speed;
        }
        
        public virtual Vector2 pursuit(Actor act)
        {
            Vector2 dist;
            dist = act.position + act.velocity * act.speed * 60 - this.position;
            if (dist != Vector2.Zero) dist.Normalize();
            return dist * this.speed;
        }

        public virtual Vector2 followPath() {
            if (wayPoints != null && wayPoints.Count > 0)
            {
                Vector2 dist;
                Map mapa = Game1.getInstance().map;
                dist = (wayPoints.First()+new Vector2(mapa.NODE_WIDTH/2,mapa.NODE_HEIGHT/2)) - this.position;
                if (dist.LengthSquared() < 100/*mapa.NODE_WIDTH*mapa.NODE_WIDTH*/)
                {
                    wayPoints.RemoveAt(0);
                } 
                if (dist != Vector2.Zero) dist.Normalize();
                return dist * this.maxSpeed;
            }
            return new Vector2(0,0);
        }
        public virtual void setWaypoints(List<Node> nodes)
        {
            if (nodes != null)
            {
                if (wayPoints == null)
                    wayPoints = new List<Vector2>();
                foreach (Node n in nodes)
                {
                    Map mapa = Game1.getInstance().map;
                    wayPoints.Add(n.mapPosition * mapa.NODE_WIDTH);
                }
            }
        }

        public virtual Vector2 repulsion()
        {
            ActorManager actorManager = ActorManager.getInstance();
            Vector2 totalRepulsion = new Vector2(0, 0);
            foreach (Actor actor in actorManager.actors)
            {
                if (actor != this && (this.position - actor.position).LengthSquared() < rangoRepulsion * rangoRepulsion)
                {
                    Vector2 distancia = this.position - actor.position;

                    float longitud = distancia.Length();
                    distancia.Normalize();
                    totalRepulsion += rangoRepulsion * distancia / longitud;
                }
            }
            return totalRepulsion;
        }
    }
}
